package menus.zone;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Point2D;

import components.intertitles.Trial;
import components.intertitles.TrialInstructions;

import menus.common.AbstractItem;
import menus.common.AbstractMenu;
import menus.common.TapAndStrokeMenu;
import menus.common.statemachines.IStateMachine;
import fr.inria.insitu.noe.Platform;
import fr.lri.swingstates.canvas.CPolyLine;
import fr.lri.swingstates.canvas.CStateMachine;
import fr.lri.swingstates.canvas.Canvas;
import fr.lri.swingstates.gestures.Gesture;
import fr.lri.swingstates.sm.State;
import fr.lri.swingstates.sm.Transition;
import fr.lri.swingstates.sm.transitions.Drag;
import fr.lri.swingstates.sm.transitions.Press;
import fr.lri.swingstates.sm.transitions.Release;
import fr.lri.swingstates.sm.transitions.TimeOut;

public class ZoneMenuStateMachine extends CStateMachine implements IStateMachine {
	
	/**
	 * Label for the "inactive" state
	 */
	protected static final String INACTIVE_STATE = "inactif";
	
	/**
	 * Label for the "selection" state
	 */
	protected static final String SELECTION_STATE = "selection";
	
	/**
	 * Label for the "triggered" state
	 */
	protected static final String TRIGGERED_STATE = "triggered";
	
	
	
	/**
	 * Menu to which is attached this state machine
	 */
	protected AbstractMenu menu;
	
	/**
	 * Gesture made by the user, used to determine the selected item (if any)
	 */
	protected Gesture gesture;
	
	/**
	 * Mark drawn by the user. Only for visual feedback.
	 */
	protected CPolyLine mark;
	
	/**
	 * Location of the menu for its last call.
	 */
	protected Point2D where;
	
	/**
	 * Selected item
	 */
	protected AbstractItem selected;
	
	
	
	/**
	 * Temporal threshold. 
	 * TODO Not used right now
	 */
	protected int startTreshold = 1000;
	
	/**
	 * Timestamp of the selection beginning
	 */
	protected long selectionStart = -1;
	
	/**
	 * Duration of the selection
	 */
	protected long selectionDuration = -1;
	
	/**
	 * False if a selection is occurring, true either
	 * Used for the experiment
	 */
	protected boolean selectionEnded = false;
	
	protected static final String EXPERT_STATE = "expert";
	
	protected int timeOut = 1000;
	
	
	
	// ----------------------------------------------------------------- XXX

	
	
	
	/**
	 * Initial state. Waits for the selection to begin.
	 */
	public State inactive = new State(INACTIVE_STATE){
		
		public Transition startSelection = new Press(">> " + TRIGGERED_STATE){
			
			@Override
			public void action() {
				
				// Initializing the mark to the event location and showing it
				mark.reset(getPoint());
				mark.setDrawable(true);
				
				// Also initializing the gesture
				gesture.addPoint(getPoint().getX(), getPoint().getY());
				
				// ...and finally initializing the location of the menu
				where = getPoint();
				
				selectionStart = System.currentTimeMillis();
				
				menu.getCanvas().removeShapes(menu.getCanvas().getTag(TrialInstructions.InstructionTag));
				
			}
			
		};
		
		@Override
		public void enter() {
			
			// Resetting the gesture and hiding the mark
			gesture.reset();
			mark.setDrawable(false);
			
		}
		
	};
	
	/**
	 * Intermediate state. Used to differentiate the different selection ways
	 * (one-stroke, tap-and-stroke, etc.)
	 */
	public State triggered = new State(TRIGGERED_STATE){
		
		public Transition triggered_release = new Release(">> " + EXPERT_STATE){
			
			@Override
			public void action() {
				((TapAndStrokeMenu)menu).getTap().translateTo(where.getX(), where.getY())
					.setDrawable(true);
			}
			
		};
		
		@Override
		public void enter() {}
		
	};
	
	public State selection = new State(SELECTION_STATE){
		
		public Transition selection_drag = new Drag(){
			
			@Override
			public void action() {
				mark.lineTo(getPoint());
				gesture.addPoint(getPoint().getX(), getPoint().getY());
				selectionEnded = true;
			}
			
		};
		
		public Transition selection_release = new Release(">> " + INACTIVE_STATE){
			
			@Override
			public void action() {
				
				//if (!selectionEnded){
					((TapAndStrokeMenu)menu).getTap().setDrawable(false);
				//	System.out.println("Hide tap !!");
				//}
				
				endSelection();
				
			}
			
		};
		
		@Override
		public void enter() {
		}
		
		@Override
		public void leave() {
			
		}
		
	};
	
	public State expert = new State(EXPERT_STATE){
		
		@Override
		public void enter() {
			armTimer(timeOut, false);
		};
		
		Transition startDrawing = new Press(">> " + SELECTION_STATE){
			@Override
			public void action() {
				
				mark.reset(getPoint());
				mark.setDrawable(true);
				
				gesture.reset();
				gesture.addPoint(getPoint().getX(), getPoint().getY());
			}
		};
		
		Transition cancel = new TimeOut(">> " + INACTIVE_STATE){
			
			@Override
			public void action() {
				((TapAndStrokeMenu)menu).getTap().setDrawable(false);
				selectionEnded = false;
			}
			
		};
		
	};
	
	
	
	// ----------------------------------------------------------------- XXX

	
	
	/**
	 * Default constructor
	 * @param canvas
	 * @param menu
	 */
	public ZoneMenuStateMachine(Canvas canvas, AbstractMenu menu){
		
		super(canvas);
		
		this.menu = menu;
		
		this.gesture = new Gesture();
		
		this.mark = menu.getCanvas().newPolyLine();
		this.mark.setAntialiased(true)
				.setFilled(false)
				.setOutlinePaint(Color.blue)
				.setStroke(new BasicStroke(3))
				.setDrawable(false)
				.setPickable(false)
				.addTag(Trial.UtilTag);
		
		this.initialState = this.currentState = inactive;
		
	}
	
	/**
	 * @param canvas
	 * @param menu
	 * @param treshold
	 */
	public ZoneMenuStateMachine(Canvas canvas, AbstractMenu menu, int treshold){
		
		this(canvas, menu);
		this.startTreshold = treshold;
		
	}
	
	/**
	 * Method calling the ghost() method of the specified item.
	 * @param where
	 * @param selected
	 */
	protected void callGhost(Point2D where, AbstractItem selected){
		
		if (selected != null && where != null)
			selected.ghost(where);
		else {
			if (selected == null)
				System.out.println("selected null");
			if (where == null)
				System.out.println("where null");
		}
		
	}
	
	/**
	 * Method calling the ghost() method of the selected item.
	 * @param where
	 */
	protected void callGhost(Point2D where){
		callGhost(where, selected);
	}
	
	/**
	 * @return
	 */
	public CPolyLine getMark(){
		return mark;
	}
	
	public long[] getSelectionTimes(){
		return new long[]{selectionStart, selectionDuration};
	}
	
	public void resetSelection(){
		selectionEnded = false;
	}
	
	public boolean isSelectionEnded(){
		return selectionEnded;
	}
	
	@Override
	public String toString() {
		String result = "\n";
		
		result += "State Machine :\n";
		for (State s : this.getAllStates()){
			result += "\tState " + s.getName() + "\n";
			for (Transition t : s.getTransitions()){
				result += "\t\t" + t.toString() + "\n";
			}
		}
		result += "\n";
		
		return result;
	}

	@Override
	public void doSuspend() {
		endSelection();
	}
	
	public void endSelection() {
		
		mark.setDrawable(false);
		
		selected = ((TapAndStrokeMenu)menu).getItem(where, gesture);
		
		callGhost(where);
		
		selectionEnded = true;
		
		selectionDuration = System.currentTimeMillis() - selectionStart;
		
		((Trial)Platform.getExperiment().getCurrentBlock()).setMeasureValues();
	}
	
}
